//-------------------------------------------------------
// Comments & File Logging
//-------------------------------------------------------
/**
* @file
* @brief This function handles all client related functions and interacts with client.
* @author Jerry, Soon, Hannah
* @date 26th January 2014
*
*/

//-------------------------------------------------------
// Header Files
//-------------------------------------------------------
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // for EXIT_FAILURE
#include "storage.h" // Defines max len for inputs
#include "utils.h"
#include <sys/time.h>

//-------------------------------------------------------
// Constants
//-------------------------------------------------------
#define LOGGING 2
enum {Connect = 1, Authenticate = 2, Get = 3, Set = 4, Disconnect = 5, Exit = 6};
char inputstr[] ="Please input the";

//-------------------------------------------------------
// Main
//-------------------------------------------------------
/**
 * Main function initializes the client
 * and deals with parsing user input
*/
int main(int argc, char *argv[])
{
    // Set up time for logging
    char fileName[MAX_STRTYPE_SIZE];
    time_t basetime;
    struct tm * conTime;
    basetime = time(NULL);
    conTime = localtime(&basetime);
    strftime(fileName, sizeof(fileName), "Client-%Y-%m-%d-%H-%M-%S.log",conTime);
    // Performance Evaluation Variables
    struct timeval start_time, end_time;
    int tempT = 0; // change in time
    // Set up logging
    int logChoice = LOGGING;
    switch(logChoice)
    {
	case 0: // Disable Logging
	    fileClient = NULL;
	    break;
	case 1: // Log to stdout
	    fileClient = stdout;
	    break;
	case 2: // Log to File
	    // Append file for appending an update
	    fileClient = fopen (fileName,"a+");
	    if (fileClient == NULL)
	    {
		printf("Error opening file.\n");
		fclose(fileClient);
		exit(EXIT_FAILURE);
	    }
	    break;
    }
    // Variables (add 1 for '/0' character)
    char username[MAX_USERNAME_LEN+1], password[MAX_ENC_PASSWORD_LEN+1],
	 hostname[MAX_HOST_LEN+1], port[MAX_PORT_LEN+1],
	 table[MAX_TABLE_LEN+1], key[MAX_KEY_LEN+1];
    int portNum = 0;
    int choice = 0;
    int status;
    void *conn;
    struct storage_record r; // the structure type for the record value
    // Client Interface
    while(1)
    {
	// Displaying a menu
	menu();
	scanf("%d", &choice);
	// Handling garbage inputs (non-numbers)
	int c;
	while((c = getchar()) != EOF)
    		if (c == '\n')
        		break;
	// @note: This will be an error if user did not enter a number
	//	  in scanf, it is fxed with the above code that
	//	  empties the stdin buffer
	switch(choice)
	{
	    // TODO: 2nd call onwards to connect() should result in error in storage.c
	    //-----------------------------------------
	     case Connect:
	    //------------------------------------------
		printf("%s hostname: ", inputstr);
		// Get the next line entered, ensures that line entered does not exceed hostname's length
                if(!getLine(hostname, sizeof(hostname))) // defined in utils.h
                {
                    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(hostname)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
                }
		printf("%s port: ", inputstr);
		if (!scanf("%d",&portNum))
		{
	             while((c = getchar()) != EOF)
            	     if (c == '\n')
			break;
		    perror("Error: Please enter a number\n");
		    errno = ERR_INVALID_PARAM;
		    break; // exit
		}
		// Ensure no other characters, handle cases like: 1376 a
		// In other words, extra string after number
                while((c = getchar()) != EOF)
		{
		    if (c == '\n')
			break;
		    else
		    {
			// If error occurs
			if ( c!= ' ')
			{
			    // Clear buffer
			    while((c = getchar()) != EOF)
				if (c == '\n')
				    break;
			    perror("Error: Please enter a number\n");
			    errno = ERR_INVALID_PARAM;
			    break;
			}
		    }
		}
		// Connect to server, conn points to sockfd returned
		// Start timer for performance evaluation
		gettimeofday(&start_time, NULL);
		conn = storage_connect(hostname, portNum);
		if(!conn)
		{
		    sprintf(message, "Cannot connect to server @ %s:%d. Error code: %d.\n", hostname, portNum, errno);
		    printf("%s", message);
		    logger(fileClient, message);
		    errno = ERR_CONNECTION_FAIL;
		    return -1;
	    	}
		// Calculate Performance time
		gettimeofday(&end_time, NULL);		//End of time
		tempT = ((end_time.tv_sec - start_time.tv_sec)*1000000L+end_time.tv_usec) - start_time.tv_usec;
			//Total time (endtime.seconds-starttime.seconds)*1000000+endtime.microseconds-starttime.microseconds)
		sprintf(message, "%d: Connect Time in micros\n", tempT);	//Convert to string
		logger(fileClient, message);		//Log
		break;
	    //------------------------------------------
	    case Authenticate:
	    //------------------------------------------
		printf("%s username: ", inputstr);
		if(!getLine(username, sizeof(username)))
		{
		    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(username)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
		}
		printf("%s password: ", inputstr);
                if(!getLine(password, sizeof(password)))
                {
                    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(password)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
                }
	    	// Authenticate the client, sends an authenticated message out to server
		gettimeofday(&start_time, NULL);
	    	status = storage_auth(username, password, conn);
	    	if(status != 0)
	    	{
//		    errno = ERR_NOT_AUTHENTICATED;
		    sprintf(message, "storage_auth failed with username '%s' and password '%s'. " \
		   "Error code: %d.\n", username, password, errno);
		    printf("%s", message);
		    logger(fileClient, message);
		    storage_disconnect(conn);
		    return status;
	    	}
		// Performance Evaluation
		gettimeofday(&end_time, NULL);
		tempT = ((end_time.tv_sec - start_time.tv_sec)*1000000L+end_time.tv_usec) - start_time.tv_usec;	
		sprintf(message, "%d: Auth Time in micros\n", tempT);
		logger(fileClient, message);

	    	// Client prints storage_auth: successful
	    	sprintf(message, "storage_auth: successful.\n");
		printf("%s", message);
		logger(fileClient, message);
		break;
	    //------------------------------------------
	    case Get:
	    //------------------------------------------
		printf("%s key: ", inputstr);
                if(!getLine(key, sizeof(key)))
                {
                    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(key)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
                }
		printf("%s table: ", inputstr);
                if(!getLine(table, sizeof(table)))
                {
                    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(table)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
                }

	 	// Issue storage_get and print message to server
		// and changes value of r from "some_value" to "GET marks ece297"
		gettimeofday(&start_time, NULL);
		status = storage_get(table, key, &r, conn);
		if(status != 0)
		{
		    sprintf(message, "storage_get failed. Error code: %d.\n", errno);
		    printf("%s", message);
		    logger(fileClient, message);
		    storage_disconnect(conn);
		    return status;
		}
		gettimeofday(&end_time, NULL);
		tempT = ((end_time.tv_sec - start_time.tv_sec)*1000000L+end_time.tv_usec) - start_time.tv_usec;	
		sprintf(message, "%d: Get Time in micros\n", tempT);
		logger(fileClient, message);
		sprintf(message, "storage_get: the value returned for key '%s' is '%s'.\n", key, r.value);
		printf("%s", message);
		logger(fileClient, message);
		break;
	    //------------------------------------------
	    case Set:
	    //------------------------------------------
		printf("%s key: ", inputstr);
                if(!getLine(key, sizeof(key)))
                {
                    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(key)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
                }
		printf("%s table: ", inputstr);
                if(!getLine(table, sizeof(table)))
                {
                    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(table)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
                }
		printf("%s value: ", inputstr);
                if(!getLine(r.value, sizeof(r.value)))
                {
                    sprintf(message, "Input given exceed maximum length of %d\n", sizeof(r.value)-1);
                    printf("%s", message);
                    logger(fileClient, message);
                    errno = ERR_INVALID_PARAM;
                    return -1;
                }

		// Create a storage_record r that stores
		// Issue storage_set to store a value with its associated key
		// Currently just prints out to screen that it has set storage
		gettimeofday(&start_time, NULL);
		status = storage_set(table, key, &r, conn);
		if(status != 0)
		{
		    sprintf(message, "storage_set failed. Error code: %d.\n", errno);
		    printf("%s", message);
		    logger(fileClient, message);
		    storage_disconnect(conn);
		    return status;
		}
		gettimeofday(&end_time, NULL);
		tempT = ((end_time.tv_sec - start_time.tv_sec)*1000000L+end_time.tv_usec) - start_time.tv_usec;	
		sprintf(message, "%d: Set Time in micros\n", tempT);
		logger(fileClient, message);
		sprintf(message, "storage_set: successful.\n");
		printf("%s", message);
		logger(fileClient, message);
		break;
	    //------------------------------------------
	    case Disconnect:
	    //------------------------------------------
		// Disconnect from server
		gettimeofday(&start_time, NULL);
		status = storage_disconnect(conn);
		if(status != 0)
		{
		    sprintf(message, "storage_disconnect failed. Error code: %d.\n", errno);
		    printf("%s", message);
		    logger(fileClient, message);
		    return status;
		}
		gettimeofday(&end_time, NULL);
		tempT = ((end_time.tv_sec - start_time.tv_sec)*1000000L+end_time.tv_usec) - start_time.tv_usec;	
		sprintf(message, "%d: Disconnect Time in micros\n", tempT);
		logger(fileClient, message);
		break;
	    //------------------------------------------
	    case Exit:
	    //------------------------------------------
	        // Exit the client
		return 0;
		break;
	    //------------------------------------------
	    default: // Invalid Input
	    //------------------------------------------
		printf("Please enter a valid selection\n");
		break;
	}
    }
}
